<?php
	/**
	 * Markup node.
	 * @author IMP
	 */
	
	class MarkupNode extends MarkupElement implements Iterator, ArrayAccess, SeekableIterator, Serializable, Countable {
		private $tagName, $children = array(), $attributes = array();
		
		public function __construct($tag){
			$this->tagName = $tag;
		}
		
		public function addChild(MarkupElement &$child){
			$this->children[] = $child;
		}
		
		public function getChildren(){
			return $this->children;
		}
		
		public function getTagName(){
			return $this->tagName;
		}
		
		public function getInnerHTML(){
			return implode('', $this->children);
		}
		
		public function hasAttribute($attr){
			return $this->offsetExists($attr);
		}
		
		public function setAttribute($attr, $value){
			$this->offsetSet($attr, $value);
		}
		
		public function getAttribute($attr, $default = null){
			if($this->hasAttribute($attr)){
				return $this->offsetGet($attr);
			}else{
				return $default;
			}
		}
		
		private function emitAttributes(){
			$attributes = array();
			foreach($this->attributes as $k => $v){
				$attributes[] = $k . '="' . $v . '"';
			}
			
			return implode(' ', $attributes);
		}
		
		public function emit(){
			return '<' . $this->getTagName() . (count($this->attributes) > 0 ? ' ' : '') . $this->emitAttributes() . '>' . $this->getInnerHTML() . '</' . $this->getTagName() . '>';
		}
		
		/* Interface Implementations */
		private $pos = 0;
		
		public function current(){
			$keys = array_keys($this->attributes);
			return $this->attributes[$keys[$this->pos]];
		}
		
		public function key(){
			$keys = array_keys($this->attributes);
			return $keys[$this->pos];
		}
		
		public function next(){
			$this->pos++;
		}
		
		public function rewind(){
			$this->pos = 0;
		}
		
		public function valid(){
			return $this->pos >= 0 && $this->pos < count($this->attributes);
		}
		
		public function offsetExists($offset){
			return isset($this->attributes[$offset]);
		}
		
		public function offsetUnset($offset){
			if($this->offsetExists($offset)){
				unset($this->attributes[$offset]);
			}else{
				throw new OutOfBoundsException('Offset ' . $offset . ' does not exist.');
			}
		}
		
		public function offsetSet($offset, $value){
			if(is_null($offset)){
				$this->attributes[] = $value;
			}else{
				$this->attributes[$offset] = $value;
			}
		}
		
		public function offsetGet($offset){
			if($this->offsetExists($offset)){
				return $this->attributes[$offset];
			}else{
				throw new OutOfBoundsException('Offset ' . $offset . ' does not exist.');
			}
		}
		
		public function count(){
			return count($this->attributes);
		}
		
		public function serialize(){
			return serialize($this->attributes);
		}
		
		public function unserialize($str){
			$uns = unserialize($str);
			
			if(is_array($uns)){
				$this->attributes = $uns;
			}else{
				throw new Exception('Invalid serialization format.');
			}
		}
		
		public function seek($pos){
			$this->pos = $pos;
			
			if(!$this->valid()){
				throw new OutOfBoundsException('Position ' . $pos . ' does not exist.');
			}
		}
		
		public function __set($name, $value){
			$this->offsetSet($name, $value);
		}
		
		public function __get($name){
			return $this->offsetGet($name);
		}
	}
?>